package com.horay.nettydemo.nio.socket;

import lombok.extern.slf4j.Slf4j;
import org.springframework.util.unit.DataSize;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.StandardCharsets;
import java.util.Iterator;
import java.util.Objects;
import java.util.Scanner;
import java.util.Set;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * Nio客户端
 *
 * @author heyingcheng
 * @date 2019/11/24 16:35
 */
@Slf4j
public class NioClient {

    public static void main(String[] args) {
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1, 1, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>());
        try (SocketChannel socketChannel = SocketChannel.open()) {
            InetSocketAddress inetSocketAddress = new InetSocketAddress("127.0.0.1", 6666);
            socketChannel.configureBlocking(false);

            Selector selector = Selector.open();

            socketChannel.register(selector, SelectionKey.OP_READ);

            if (!socketChannel.connect(inetSocketAddress)) {
                while (!socketChannel.finishConnect()) {
                    log.info("Registering, Do something");
                }
            } else {
                log.error("未能连接服务器, hostName:[{}], port:[{}]", inetSocketAddress.getHostName(), inetSocketAddress.getPort());
                System.exit(0);
            }

            threadPoolExecutor.execute(() -> {
                for (; ; ) {
                    try {
                        int op = selector.select(TimeUnit.SECONDS.toMillis(1));
                        if (op == 0) {
                            long l = System.currentTimeMillis();
                            if (l % 31 == 0) {
                                log.info("未监听到操作类型");
                            }
                        }

                        Set<SelectionKey> selectionKeys = selector.selectedKeys();
                        Iterator<SelectionKey> iterator = selectionKeys.iterator();
                        for (; iterator.hasNext(); ) {
                            SelectionKey selectionKey = iterator.next();
                            iterator.remove();

                            if (selectionKey.isReadable()) {
                                SocketChannel channel = null;
                                try {
                                    channel = (SocketChannel) selectionKey.channel();
                                    ByteBuffer byteBuffer = ByteBuffer.allocate(Math.toIntExact(DataSize.ofKilobytes(1).toBytes()));
                                    channel.read(byteBuffer);

                                    byteBuffer.flip();

                                    CharsetDecoder charsetDecoder = StandardCharsets.UTF_8.newDecoder();
                                    String message = charsetDecoder.decode(byteBuffer).toString();
                                    log.info("客户端:[{}], 响应消息:[{}]", channel.hashCode(), message);
                                } catch (IOException e) {
                                    log.error("读取服务端消息异常", e);
                                    if (Objects.nonNull(channel)) {
                                        channel.close();
                                    }
                                }
                            }
                        }
                    } catch (IOException e) {
                        log.error("读取服务器响应消息异常", e);
                    }
                }
            });

            Scanner scanner = new Scanner(System.in);
            while (scanner.hasNext()) {
                String message = scanner.nextLine();
                ByteBuffer byteBuffer = ByteBuffer.wrap(message.getBytes());
                socketChannel.write(byteBuffer);
            }


        } catch (IOException e) {
            log.error("服务器启动异常", e);
        }

    }

}
